package com.lap.auth.adapter.driven.aop;

import com.lap.auth.application.authentication.command.LoginCmd;
import com.lap.auth.domain.login.event.LoginLogEvent;
import com.lap.context.starter.core.helper.MessageHelper;
import com.lap.framework.common.enums.BoolEnum;
import com.lap.framework.common.exception.BizException;
import com.lap.web.starter.ip.ClientInfoResolver;
import com.lap.web.starter.ip.valueobject.ClientInfo;
import jakarta.annotation.Resource;
import java.util.concurrent.TimeUnit;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
public class LoginLogAspect {

  @Resource private ApplicationEventPublisher publisher;
  @Resource private MessageHelper messageHelper;
  @Resource private HttpServletRequest request;

  @Pointcut("@annotation(com.lap.auth.shared.annotations.LoginLog)")
  public void loginPointcut() {}

  @Around("loginPointcut()")
  public Object around(ProceedingJoinPoint point) throws Throwable {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    BoolEnum success = BoolEnum.YES;
    String message = "Success";
    String userName = getUserName(point.getArgs());

    try {
      return point.proceed();
    } catch (Throwable e) {
      success = BoolEnum.NO;
      message = getRemark(e);
      throw e;
    } finally {
      stopWatch.stop();
      long millis = stopWatch.getTime(TimeUnit.MILLISECONDS);
      ClientInfo info = ClientInfoResolver.resolve(request);
      publisher.publishEvent(
          new LoginLogEvent(
              userName,
              millis,
              success,
              message,
              info.ip(),
              info.userAgent().os(),
              info.userAgent().browser(),
              info.userAgent().device()));
    }
  }

  private String getRemark(Throwable e) {
    return (e instanceof BizException biz)
        ? messageHelper.getMsg(biz.getErrorCode().getMessageKey(), biz.getArgs())
        : e.getLocalizedMessage();
  }

  private String getUserName(Object[] arg) {
    return (ArrayUtils.isNotEmpty(arg) && arg[0] instanceof LoginCmd cmd)
        ? cmd.getUserName()
        : null;
  }
}
